package eval

import (
	"showen7/eval"
	"showen7/inter"
	"showen7/lex"
	"showen7/parse"
	"testing"
)

func TestAddOperation(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		// Basic integer addition
		{
			"(+ 1 1)",
			[]string{"2"},
		},
		{
			"(+ 5 3 2)",
			[]string{"10"},
		},
		{
			"(+ -3 7)",
			[]string{"4"},
		},
		{
			"(+ 0 5)",
			[]string{"5"},
		},
		
		// BigInteger addition (large numbers that overflow int64)
		{
			"(+ 12345678901234567890 98765432109876543210)",
			[]string{"111111111011111111100"},
		},
		{
			"(+ 12345678901234567890 -12345678901234567890)",
			[]string{"0"},
		},
		
		// Rational addition
		{
			"(+ 1/2 1/3)",
			[]string{"5/6"},
		},
		{
			"(+ 3/4 1/4)",
			[]string{"1"},
		},
		{
			"(+ 2/3 1/6 1/2)",
			[]string{"4/3"},
		},
		{
			"(+ -1/2 3/4)",
			[]string{"1/4"},
		},
		
		// Real (float) addition
		{
			"(+ 1.5 2.5)",
			[]string{"4"},
		},
		{
			"(+ 3.14159 2.71828)",
			[]string{"5.85987"},
		},
		{
			"(+ -1.5 1.5)",
			[]string{"0"},
		},
		
		// Complex addition
		{
			"(+ 1+2i 3+4i)",
			[]string{"4+6i"},
		},
		{
			"(+ 2+3i -1-2i)",
			[]string{"1+1i"},
		},
		{
			"(+ 1+2i 3+4i 5+6i)",
			[]string{"9+12i"},
		},
		{
			"(+ +i -i)",
			[]string{"0+0i"},
		},
		
		// Mixed type addition
		// Integer + Rational
		{
			"(+ 1 1/2)",
			[]string{"3/2"},
		},
		{
			"(+ 2 3/4)",
			[]string{"11/4"},
		},
		
		// Integer + Real
		{
			"(+ 1 1.5)",
			[]string{"2.5"},
		},
		{
			"(+ 3 2.7)",
			[]string{"5.7"},
		},
		
		// Integer + Complex
		{
			"(+ 2 1+2i)",
			[]string{"3+2i"},
		},
		{
			"(+ 5 3+4i)",
			[]string{"8+4i"},
		},
		
		// Rational + Real - Now returns Real
		{
			"(+ 1/2 0.5)",
			[]string{"1"},
		},
		{
			"(+ 3/4 0.25)",
			[]string{"1"},
		},
		
		// Rational + Complex - Now returns Complex
		{
			"(+ 1/2 1+2i)",
			[]string{"1.5+2i"},
		},
		{
			"(+ 3/4 2+1i)",
			[]string{"2.75+1i"},
		},
		// Rational + Complex - Complex part is preserved when complex is first
		{
			"(+ 1+2i 1/2)",
			[]string{"1.5+2i"},
		},
		{
			"(+ 2+1i 3/4)",
			[]string{"2.75+1i"},
		},
		
		// Real + Complex - Complex part is preserved regardless of order
		{
			"(+ 1.5 1+2i)",
			[]string{"2.5+2i"},
		},
		{
			"(+ 1+2i 2.7)",
			[]string{"3.7+2i"},
		},
		
		// BigInteger + other types
		{
			"(+ 12345678901234567890 1)",
			[]string{"12345678901234567891"},
		},
		{
			"(+ 12345678901234567890 1/2)",
			[]string{"24691357802469135781/2"},
		},
		{
			"(+ 12345678901234567890 1.5)",
			[]string{"1.23456789e+19"},
		},
		
		// Edge cases
		{
			"(+)",
			[]string{"0"},
		},
		{
			"(+ 5)",
			[]string{"5"},
		},
		{
			"(+ 0 0 0)",
			[]string{"0"},
		},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := eval.EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			// Since the lexer converts identifiers to uppercase,
			// we need to adjust our expectation accordingly
			expected := tt.want[i]
			if obj.Inspect() != expected {
				t.Errorf("Eval(%s) = %v, want %v", tt.input, obj.Inspect(), expected)
			}
		}
	}
}

func TestSubtractOperation(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		// Basic integer subtraction
		{
			"(- 5 3)",
			[]string{"2"},
		},
		{
			"(- 1 5)",
			[]string{"-4"},
		},
		{
			"(- 10 3 2)",
			[]string{"5"},
		},
		{
			"(- 0 5)",
			[]string{"-5"},
		},
		
		// Unary subtraction (negation)
		{
			"(- 5)",
			[]string{"-5"},
		},
		{
			"(- -3)",
			[]string{"3"},
		},
		
		// Rational subtraction
		{
			"(- 1/2 1/3)",
			[]string{"1/6"},
		},
		{
			"(- 3/4 1/4)",
			[]string{"1/2"},
		},
		{
			"(- 2/3 1/6 1/2)",
			[]string{"0"},
		},
		
		// Real (float) subtraction
		{
			"(- 2.5 1.5)",
			[]string{"1"},
		},
		{
			"(- 3.14159 2.71828)",
			[]string{"0.42331"},
		},
		
		// Complex subtraction
		{
			"(- 3+4i 1+2i)",
			[]string{"2+2i"},
		},
		{
			"(- 2+3i -1-2i)",
			[]string{"3+5i"},
		},
		
		// Unary negation of complex numbers
		{
			"(- 1+2i)",
			[]string{"-1-2i"},
		},
		
		// Mixed type subtraction
		// Integer - Rational
		{
			"(- 2 1/2)",
			[]string{"3/2"},
		},
		
		// Integer - Real
		{
			"(- 3 1.5)",
			[]string{"1.5"},
		},
		
		// Integer - Complex
		{
			"(- 5 3+4i)",
			[]string{"2-4i"},
		},
		
		// Rational - Real
		{
			"(- 1/2 0.25)",
			[]string{"0.25"},
		},
		
		// Rational - Complex
		{
			"(- 3/4 1+2i)",
			[]string{"-0.25-2i"},
		},
		
		// Real - Complex
		{
			"(- 2.5 1+2i)",
			[]string{"1.5-2i"},
		},
		
		// Edge cases
		{
			"(-)",
			[]string{"0"},
		},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := eval.EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			expected := tt.want[i]
			if obj.Inspect() != expected {
				t.Errorf("Eval(%s) = %v, want %v", tt.input, obj.Inspect(), expected)
			}
		}
	}
}

func TestMultiplyOperation(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		// Basic integer multiplication
		{
			"(* 2 3)",
			[]string{"6"},
		},
		{
			"(* 5 3 2)",
			[]string{"30"},
		},
		{
			"(* -3 7)",
			[]string{"-21"},
		},
		{
			"(* 0 5)",
			[]string{"0"},
		},
		
		// No arguments - return 1
		{
			"(*)",
			[]string{"1"},
		},
		
		// One argument - return that number
		{
			"(* 5)",
			[]string{"5"},
		},
		
		// Rational multiplication
		{
			"(* 1/2 1/3)",
			[]string{"1/6"},
		},
		{
			"(* 3/4 4/3)",
			[]string{"1"},
		},
		{
			"(* 2/3 3/4 4/5)",
			[]string{"2/5"},
		},
		{
			"(* -1/2 3/4)",
			[]string{"-3/8"},
		},
		
		// Real (float) multiplication
		{
			"(* 1.5 2.0)",
			[]string{"3"},
		},
		{
			"(* 3.14159 2)",
			[]string{"6.28318"},
		},
		
		// Complex multiplication
		{
			"(* 1+2i 3+4i)",
			[]string{"-5+10i"},
		},
		{
			"(* 2+3i 2-3i)",
			[]string{"13+0i"},
		},
		
		// Mixed type multiplication
		// Integer * Rational
		{
			"(* 2 1/2)",
			[]string{"1"},
		},
		
		// Integer * Real
		{
			"(* 3 1.5)",
			[]string{"4.5"},
		},
		
		// Integer * Complex
		{
			"(* 2 1+2i)",
			[]string{"2+4i"},
		},
		
		// Rational * Real
		{
			"(* 1/2 0.5)",
			[]string{"0.25"},
		},
		
		// Rational * Complex
		{
			"(* 1/2 1+2i)",
			[]string{"0.5+1i"},
		},
		
		// Real * Complex
		{
			"(* 2.5 1+2i)",
			[]string{"2.5+5i"},
		},
		
		// BigInteger multiplication
		{
			"(* 123456789012345 2)",
			[]string{"246913578024690"},
		},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := eval.EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			expected := tt.want[i]
			if obj.Inspect() != expected {
				t.Errorf("Eval(%s) = %v, want %v", tt.input, obj.Inspect(), expected)
			}
		}
	}
}

func TestDivideOperation(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		// Basic integer division
		{
			"(/ 6 3)",
			[]string{"2"},
		},
		{
			"(/ 10 2 5)",
			[]string{"1"},
		},
		{
			"(/ -21 7)",
			[]string{"-3"},
		},
		
		// Unary division (reciprocal)
		{
			"(/ 2)",
			[]string{"1/2"},
		},
		{
			"(/ 1/2)",
			[]string{"2"},
		},
		{
			"(/ -4)",
			[]string{"-1/4"},
		},
		
		// Rational division
		{
			"(/ 1/2 1/3)",
			[]string{"3/2"},
		},
		{
			"(/ 3/4 1/4)",
			[]string{"3"},
		},
		{
			"(/ 2/3 3/4)",
			[]string{"8/9"},
		},
		
		// Real (float) division
		{
			"(/ 3.0 2.0)",
			[]string{"1.5"},
		},
		{
			"(/ 6.28318 2)",
			[]string{"3.14159"},
		},
		
		// Complex division
		{
			"(/ 1+2i 1+1i)",
			[]string{"1.5+0.5i"},
		},
		{
			"(/ 2+3i 1+0i)",
			[]string{"2+3i"},
		},
		
		// Unary division of complex numbers
		{
			"(/ 1+2i)",
			[]string{"0.2-0.4i"},
		},
		
		// Mixed type division
		// Integer / Rational
		{
			"(/ 2 1/2)",
			[]string{"4"},
		},
		
		// Integer / Real
		{
			"(/ 3 1.5)",
			[]string{"2"},
		},
		
		// Integer / Complex
		{
			"(/ 5 1+2i)",
			[]string{"1-2i"},
		},
		
		// Rational / Real
		{
			"(/ 1/2 0.25)",
			[]string{"2"},
		},
		
		// Rational / Complex
		{
			"(/ 3/4 1+2i)",
			[]string{"0.15-0.3i"},
		},
		
		// Real / Complex
		{
			"(/ 2.5 1+2i)",
			[]string{"0.5-1i"},
		},
		
		// Edge cases
		{
			"(/)",
			[]string{"1"},
		},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := eval.EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			expected := tt.want[i]
			if obj.Inspect() != expected {
				t.Errorf("Eval(%s) = %v, want %v", tt.input, obj.Inspect(), expected)
			}
		}
	}
}